package com.boil.qz.safekavass.service;

import com.boil.exception.BaseException;
import com.boil.pojo.ResultVo;
import com.boil.qz.safekavass.dao.MybatisDao;
import com.boil.qz.safekavass.model.LoginUser;
import com.boil.qz.safekavass.model.ResourceEntity;
import com.boil.qz.safekavass.model.RoleEntity;
import com.boil.qz.safekavass.pojo.*;
import com.boil.qz.safekavass.repository.ResourceRepository;
import com.boil.qz.safekavass.web.request.param.ResourceAddParam;
import com.boil.qz.safekavass.web.request.param.ResourceUpdateParam;
import com.boil.util.Constants;
import com.boil.util.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * Created by ChenYong on 2017-07-11.
 * <br>
 * 资源 Service。
 */
@Service
public class ResourceService extends JpaRepositoryService<ResourceEntity, Long> {
    /**
     * 注入 Mybatis Dao
     */
    @Autowired
    private MybatisDao mybatisDao;
    /**
     * 注入资源实体仓库
     */
    @Autowired
    private ResourceRepository resourceRepository;
    /**
     * 注入角色 Service
     */
    @Autowired
    private RoleService roleService;

    /**
     * 根据资源 ID 查询资源。
     *
     * @param id 资源 ID
     * @return 资源实例
     */
    public ResourceParentVo findById(long id) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id", id);

        return mybatisDao.selectOne("resourceVoNs.findById", map);
    }

    /**
     * 根据资源编号 <code>List</code> 查询资源实例 <code>List</code>。
     *
     * @param ids 资源编号 <code>List</code>
     * @return 资源实例 <code>List</code>
     */
    public List<ResourceEntity> findByIds(List<Long> ids) {
        return resourceRepository.findByIds(ids);
    }

    /**
     * 根据资源 ID 查询资源树。
     *
     * @param id 资源 ID
     * @return 资源树
     */
    public ResourceChildVo findByIdTree(long id) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id", id);

        return mybatisDao.selectOne("resourceVoNs.findByIdTree", map);
    }

    /**
     * 根据被排除的多个 ID 和资源类型查询资源。
     *
     * @param excludeIds    被排除的多个 ID
     * @param resourceTypes 资源类型
     * @return 资源实例 <code>List</code>
     */
    public List<ResourceVo> findByExcludeIdsAndResourceTypes(List<Long> excludeIds, List<Integer> resourceTypes) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("excludeIds", excludeIds);
        map.put("resourceTypes", resourceTypes);

        return mybatisDao.selectList("resourceVoNs.findByExcludeIdsAndResourceTypes", map);
    }

    /**
     * 查询资源的 zTree 数据。
     *
     * @param id 当前资源 ID
     * @return 资源的 zTree 数据
     */
    public List<ZTreeSimpleDataVo> findZTree(long id) {
        // 查询当前资源
        ResourceChildVo currResourceChildVo = findByIdTree(id);
        // 存放需要排除的资源 ID，即排除当前资源 ID 及其所有子资源 ID
        List<Long> excludeIds = new ArrayList<Long>();

        recursionFindIdInZTree(currResourceChildVo, excludeIds);

        List<ResourceVo> resourceVos = findByExcludeIdsAndResourceTypes(excludeIds, null);
        List<ZTreeSimpleDataVo> zTreeSimpleDataVos = new ArrayList<ZTreeSimpleDataVo>();

        for (ResourceVo resourceVo : resourceVos) {
            ZTreeSimpleDataVo zTreeSimpleDataVo = new ZTreeSimpleDataVo();
            zTreeSimpleDataVo.setId(resourceVo.getId());
            zTreeSimpleDataVo.setParentId(resourceVo.getParentId());
            zTreeSimpleDataVo.setName(resourceVo.getResourceName());
            zTreeSimpleDataVo.setOpen(Constants.VALUE_TRUE);
            zTreeSimpleDataVo.setChecked(Constants.VALUE_FALSE);
            zTreeSimpleDataVo.getData().put(Constants.RESOURCE_TYPE_STR, resourceVo.getResourceType());

            // 资源类型名称
            String resourceTypeName = Constants.EMPTY_STR;

            // 菜单
            if (resourceVo.getResourceType() == Constants.VALUE_1) {
                resourceTypeName = Constants.RESOURCE_TYPE_MENU;

                // 按钮
            } else if (resourceVo.getResourceType() == Constants.VALUE_2) {
                resourceTypeName = Constants.RESOURCE_TYPE_BUTTON;

                // 数据
            } else if (resourceVo.getResourceType() == Constants.VALUE_3) {
                resourceTypeName = Constants.RESOURCE_TYPE_DATA;
            }

            if (StringUtils.isNotBlank(resourceTypeName)) {
                zTreeSimpleDataVo.setName(String.format("%s【%s】", zTreeSimpleDataVo.getName(), resourceTypeName));
            }

            if (currResourceChildVo != null) {
                // 选中父资源
                if (resourceVo.getId().equals(currResourceChildVo.getParentId())) {
                    zTreeSimpleDataVo.setChecked(Constants.VALUE_TRUE);
                }
            }

            zTreeSimpleDataVos.add(zTreeSimpleDataVo);
        }

        return zTreeSimpleDataVos;
    }

    /**
     * 分页查询资源。
     *
     * @param page 分页实例
     */
    public void findInPage(Page<ResourceParentVo> page) {
        mybatisDao.selectInPage("resourceVoNs.findInPage", page);
    }

    /**
     * 根据删除代码查询资源。
     *
     * @param deletedCode 删除代码
     * @return 资源实例 <code>List</code>
     */
    public List<ResourceEntity> findByDeletedCode(int deletedCode) {
        return resourceRepository.findByDeletedCode(deletedCode);
    }

    /**
     * 根据资源类型和用户 ID 查询资源。
     *
     * @param resourceType 资源类型
     * @param userId       用户 ID
     * @return 资源实例 <code>List</code>
     */
    public List<ResourceEntity> findByResourceTypeAndUserId(int resourceType, long userId) {
        return resourceRepository.findByResourceTypeAndUserId(resourceType, userId);
    }

    /**
     * 根据用户 ID 查询用户菜单。
     *
     * @param userId 用户 ID
     * @return 菜单实例 <code>List</code>
     */
    public List<MenuVo> getUserMenus(long userId) {
        List<MenuVo> menuVos = new ArrayList<MenuVo>();
        List<ResourceEntity> resourceEntities = findByResourceTypeAndUserId(Constants.VALUE_1, userId);

        // 找出一级菜单
        Iterator<ResourceEntity> resourceEntityIterator1 = resourceEntities.iterator();
        while (resourceEntityIterator1.hasNext()) {
            ResourceEntity resourceEntity = resourceEntityIterator1.next();

            if (resourceEntity.getResourceLevel() == Constants.VALUE_2) {
                MenuVo menuVo = new MenuVo();
                menuVo.setId(resourceEntity.getId());
                menuVo.setParentId(resourceEntity.getParent().getId());
                menuVo.setName(resourceEntity.getResourceName());
                menuVo.setIcon(resourceEntity.getResourceIcon());
                menuVo.setLevel(resourceEntity.getResourceLevel());
                menuVo.setOrder(resourceEntity.getResourceOrder());

                if (StringUtils.isNotBlank(resourceEntity.getResourceUrl())) {
                    menuVo.setUrl(resourceEntity.getResourceUrl().substring(Constants.VALUE_1));
                }

                menuVos.add(menuVo);
            }
        }

        // 找出二级菜单
        Iterator<ResourceEntity> resourceEntityIterator2 = resourceEntities.iterator();
        while (resourceEntityIterator2.hasNext()) {
            ResourceEntity resourceEntity = resourceEntityIterator2.next();

            if (resourceEntity.getResourceLevel() == Constants.VALUE_3) {
                Iterator<MenuVo> menuVoIterator = menuVos.iterator();
                while (menuVoIterator.hasNext()) {
                    MenuVo parentMenuVo = menuVoIterator.next();

                    if (resourceEntity.getParent().getId() == parentMenuVo.getId()) {
                        MenuVo childMenuVo = new MenuVo();
                        childMenuVo.setId(resourceEntity.getId());
                        childMenuVo.setParentId(resourceEntity.getParent().getId());
                        childMenuVo.setName(resourceEntity.getResourceName());
                        childMenuVo.setIcon(resourceEntity.getResourceIcon());
                        childMenuVo.setLevel(resourceEntity.getResourceLevel());
                        childMenuVo.setOrder(resourceEntity.getResourceOrder());

                        if (StringUtils.isNotBlank(resourceEntity.getResourceUrl())) {
                            childMenuVo.setUrl(resourceEntity.getResourceUrl().substring(Constants.VALUE_1));
                        }

                        parentMenuVo.getChilds().add(childMenuVo);

                        // 终止第二层循环
                        break;
                    }
                }
            }
        }

        return menuVos;
    }

    /**
     * 根据权限代码统计资源个数。
     *
     * @param permissionCode 权限代码
     * @return 资源个数
     */
    public long countByPermissionCode(String permissionCode) {
        long count = Constants.VALUE_L_0;

        if (StringUtils.isNotBlank(permissionCode)) {
            count = resourceRepository.countByPermissionCode(permissionCode);
        }

        return count;
    }

    /**
     * 校验权限代码。
     *
     * @param resultVo       结果 Vo
     * @param permissionCode 权限代码
     */
    public void checkPermissionCode(ResultVo resultVo, String permissionCode) {
        // 权限代码为空
        if (StringUtils.isBlank(permissionCode)) {
            resultVo.setResultCode(Constants.PERMISSION_CODE_EMPTY);
            resultVo.setResultMsg(Constants.PERMISSION_CODE_EMPTY_MSG);

            // 权限代码非空
        } else {
            // 权限代码的格式正确
            if (permissionCode.matches(Constants.PERMISSION_RE)) {
                long count = countByPermissionCode(permissionCode);

                // 权限代码未存在
                if (count == Constants.VALUE_L_0) {
                    resultVo.setResultCode(Constants.SUCCESS);
                    resultVo.setResultMsg(Constants.PERMISSION_CODE_NOT_EXIST_MSG);

                    // 权限代码已存在
                } else {
                    resultVo.setResultCode(Constants.PERMISSION_CODE_EXIST);
                    resultVo.setResultMsg(Constants.PERMISSION_CODE_EXIST_MSG);
                }

                // 权限代码的格式不正确
            } else {
                resultVo.setResultCode(Constants.PERMISSION_CODE_FORMAT_ERROR);
                resultVo.setResultMsg(Constants.PERMISSION_CODE_FORMAT_ERROR_MSG);
            }

            resultVo.setResult(permissionCode);
        }
    }

    /**
     * 根据资源 URL 统计资源个数。
     *
     * @param resourceUrl 资源 URL
     * @return 资源个数
     */
    public long countByResourceUrl(String resourceUrl) {
        return resourceRepository.countByResourceUrl(resourceUrl);
    }

    /**
     * 校验资源 URL。
     *
     * @param resultVo    结果 Vo
     * @param resourceUrl 资源 URL
     */
    public void checkResourceUrl(ResultVo resultVo, String resourceUrl) {
        // 资源 URL 为空
        if (StringUtils.isBlank(resourceUrl)) {
            resultVo.setResultCode(Constants.RESOURCE_URL_EMPTY);
            resultVo.setResultMsg(Constants.RESOURCE_URL_EMPTY_MSG);

            // 资源 URL 非空
        } else {
            // 资源 URL 的格式正确
            if (resourceUrl.matches(Constants.URL_ONLY_RE)) {
                long count = countByResourceUrl(resourceUrl);

                // 资源 URL 未存在
                if (count == Constants.VALUE_L_0) {
                    resultVo.setResultCode(Constants.SUCCESS);
                    resultVo.setResultMsg(Constants.RESOURCE_URL_NOT_EXIST_MSG);

                    // 资源 URL 已存在
                } else {
                    resultVo.setResultCode(Constants.RESOURCE_URL_EXIST);
                    resultVo.setResultMsg(Constants.RESOURCE_URL_EXIST_MSG);
                }

                // 资源 URL 的格式不正确
            } else {
                resultVo.setResultCode(Constants.RESOURCE_URL_FORMAT_ERROR);
                resultVo.setResultMsg(Constants.RESOURCE_URL_FORMAT_ERROR_MSG);
            }

            resultVo.setResult(resourceUrl);
        }
    }

    /**
     * 保存资源实例。
     *
     * @param param 添加资源的请求参数
     */
    public void save(ResourceAddParam param) {
        // 如果权限代码代码已存在
        if (countByPermissionCode(param.getPermissionCode()) > Constants.VALUE_L_0) {
            throw new BaseException(Constants.PERMISSION_CODE_EXIST_MSG);
        }

        // 当前时间
        Date currTime = new Date();
        // 查询父资源
        ResourceEntity parent = findOne(param.getParentId());
        // 创建子资源
        ResourceEntity child = new ResourceEntity();

        BeanUtils.copyProperties(param, child);

        // 设置顺序
        child.setResourceOrder(getMaxOrder(parent.getChilds()) + Constants.VALUE_1);

        // 双控：父关联子
        parent.getChilds().add(child);
        // 双控：子关联父
        child.setParent(parent);
        // 角色关联【超级管理员】
        child.getRoles().add(roleService.findOne(Constants.VALUE_L_1));

        child.setResourceLevel(parent.getResourceLevel() + Constants.VALUE_1);
        child.setCreateTime(currTime);
        child.setDeletedCode(Constants.VALUE_0);
    }

    /**
     * 保存资源实例。
     *
     * @param param 添加资源的请求参数
     */
    public void update(ResourceUpdateParam param) {
        // 当前时间
        Date currTime = new Date();
        // 查询父资源
        ResourceEntity parent = findOne(param.getParentId());
        // 查询子资源
        ResourceEntity child = findOne(param.getId());

        // 如果权限代码代码已存在
        if (!param.getPermissionCode().equals(child.getPermissionCode()) && (countByPermissionCode(param.getPermissionCode()) > Constants.VALUE_L_0)) {
            throw new BaseException(Constants.PERMISSION_CODE_EXIST_MSG);
        }

        BeanUtils.copyProperties(param, child);

        // 设置顺序
        child.setResourceOrder(getMaxOrder(parent.getChilds()) + Constants.VALUE_1);

        // 双控：父关联子
        parent.getChilds().add(child);
        // 双控：子关联父
        child.setParent(parent);

        child.setCreateTime(currTime);

        recursionUpdateLevel(child);
    }

    /**
     * 删除资源，如果它有子资源的话，也将被删除！！！。
     *
     * @param id 资源 ID
     */
    public void deleteById(long id) {
        // 查询资源
        ResourceEntity resourceEntity = findOne(id);

        if (resourceEntity.getResourceType() == Constants.VALUE_0) {
            throw new BaseException("根资源不能被删除！");
        }

        // 更新删除代码为“已删除”
        recursionUpdateDeletedCode(resourceEntity, Constants.VALUE_1);

        // 存放已被删除的资源 Set
        Set<ResourceEntity> deleteResourceEntities = new LinkedHashSet<ResourceEntity>();
        // 存放角色 Set
        Set<RoleEntity> roleEntities = new LinkedHashSet<RoleEntity>();
        // 存放将要被删除的角色 Set
        Set<RoleEntity> deleteRoleEntities = new LinkedHashSet<RoleEntity>();

        // 递归找到被删除的资源
        recursionFind(resourceEntity, deleteResourceEntities);

        // 遍历被删除的资源
        Iterator<ResourceEntity> deleteResourceEntityIterator1 = deleteResourceEntities.iterator();
        while (deleteResourceEntityIterator1.hasNext()) {
            // 获取已被删除的资源
            ResourceEntity deleteResourceEntity = deleteResourceEntityIterator1.next();

            roleEntities.addAll(deleteResourceEntity.getRoles());
        }

        // 遍历角色 Set，找到将要被删除的角色
        Iterator<RoleEntity> roleEntityIterator = roleEntities.iterator();
        while (roleEntityIterator.hasNext()) {
            RoleEntity roleEntity = roleEntityIterator.next();

            /*
             判断角色是否可以删除？
             如何判断：
                因为所有的被删除资源都存放到 deleteResourceEntities 中，
                所以，当被删除的资源（deleteResourceEntities）全部包含了角色（roleEntity）拥有的所有资源，
                表示此角色（roleEntity）可以被删除，立即加入到删除队列中。
             */
            if (deleteResourceEntities.containsAll(roleEntity.getResources())) {
                deleteRoleEntities.add(roleEntity);
            }
        }

        //----------[清理操作]----------//
        // 1、解除角色与资源的关联关系：遍历被删除的资源
        Iterator<ResourceEntity> deleteResourceEntityIterator2 = deleteResourceEntities.iterator();
        while (deleteResourceEntityIterator2.hasNext()) {
            // 获取已被删除的资源
            ResourceEntity deleteResourceEntity = deleteResourceEntityIterator2.next();

            deleteResourceEntity.getRoles().clear();
        }

        // 2、解除用户与角色的关联关系：遍历将要被删除的角色
        Iterator<RoleEntity> deleteRoleEntityIterator = deleteRoleEntities.iterator();
        while (deleteRoleEntityIterator.hasNext()) {
            RoleEntity deleteRoleEntity = deleteRoleEntityIterator.next();

            Iterator<LoginUser> loginUserIterator = deleteRoleEntity.getLoginUsers().iterator();
            while (loginUserIterator.hasNext()) {
                LoginUser loginUser = loginUserIterator.next();

                loginUser.getRoles().remove(deleteRoleEntity);
            }
        }

        // 3、删除角色
        roleService.delete(deleteRoleEntities);
        //----------[/清理操作]----------//
    }

    /**
     * 获取子资源中的最大顺序。
     *
     * @param childs 子资源 <code>Set</code>
     * @return 最大顺序
     */
    private int getMaxOrder(Set<ResourceEntity> childs) {
        int maxOrder = Constants.VALUE_0;

        if (childs != null) {
            Iterator<ResourceEntity> iterator = childs.iterator();

            while (iterator.hasNext()) {
                ResourceEntity resourceEntity = iterator.next();

                if (resourceEntity.getResourceOrder() > maxOrder) {
                    maxOrder = resourceEntity.getResourceOrder();
                }
            }
        }

        return maxOrder;
    }

    /**
     * 递归找到资源实例。
     *
     * @param parent           父资源
     * @param resourceEntities 资源实例 <code>Set</code>
     */
    private void recursionFind(ResourceEntity parent, Set<ResourceEntity> resourceEntities) {
        if (parent != null) {
            resourceEntities.add(parent);

            Set<ResourceEntity> childs = parent.getChilds();
            Iterator<ResourceEntity> iterator = childs.iterator();

            while (iterator.hasNext()) {
                ResourceEntity child = iterator.next();

                recursionFind(child, resourceEntities);
            }
        }
    }

    /**
     * 递归找到资源树中的所有资源 ID。
     *
     * @param parent 父资源
     * @param ids    资源树中的所有资源 ID <code>List</code>
     */
    private void recursionFindIdInZTree(ResourceChildVo parent, List<Long> ids) {
        if (parent != null) {
            ids.add(parent.getId());

            Set<ResourceChildVo> childs = parent.getChilds();
            Iterator<ResourceChildVo> iterator = childs.iterator();

            while (iterator.hasNext()) {
                ResourceChildVo child = iterator.next();

                recursionFindIdInZTree(child, ids);
            }
        }
    }

    /**
     * 递归更新层级。
     *
     * @param parent 父资源
     */
    private void recursionUpdateLevel(ResourceEntity parent) {
        if (parent != null) {
            parent.setResourceLevel(parent.getParent().getResourceLevel() + Constants.VALUE_1);

            Set<ResourceEntity> childs = parent.getChilds();
            Iterator<ResourceEntity> iterator = childs.iterator();

            while (iterator.hasNext()) {
                ResourceEntity child = iterator.next();

                recursionUpdateLevel(child);
            }
        }
    }

    /**
     * 递归更新删除代码。
     *
     * @param parent      父资源
     * @param deletedCode 删除代码
     */
    private void recursionUpdateDeletedCode(ResourceEntity parent, int deletedCode) {
        if (parent != null) {
            parent.setDeletedCode(deletedCode);

            Set<ResourceEntity> childs = parent.getChilds();
            Iterator<ResourceEntity> iterator = childs.iterator();

            while (iterator.hasNext()) {
                ResourceEntity child = iterator.next();

                recursionUpdateDeletedCode(child, deletedCode);
            }
        }
    }
}